import { defineStore } from 'pinia'
import { reactive, computed, markRaw } from 'vue'
import * as THREE from 'three'
import { parse, compile } from 'mathjs'
import { analyzeDependencies } from '@/utils/mathUtils'
import { createParametricGeometry } from '@/utils/threeUtils'

export const useFormulaStore = defineStore('formula', () => {
  const state = reactive({
    formulas: [],
    dependencyGraph: new Map(),
    computationCache: new Map(),
    nextId: 1,
  })

  const getters = {
    sortedFormulas: computed(() => {
      const visited = new Set()
      const order = []

      const visit = (formulaId) => {
        if (!visited.has(formulaId)) {
          visited.add(formulaId)
          state.dependencyGraph.get(formulaId)?.forEach((depId) => visit(depId))
          order.push(formulaId)
        }
      }

      state.formulas.forEach((f) => visit(f.id))
      return order
        .reverse()
        .map((id) => state.formulas.find((f) => f.id === id))
    }),
  }

  const actions = {
    initialize() {
      this.addFormula({
        expression: 'z = sin(x) * cos(y)',
        color: '#ff0000',
      })
      this.addFormula({
        expression: 'z = f1(x, y+10) * 0.5 + x/2',
        color: '#00ffff',
      })
      this.addFormula({
        expression: 'x=1',
        color: '#0000ff',
      })
    },

    addFormula(config) {
      const newFormula = reactive({
        id: `f${state.nextId++}`,
        expression: config.expression,
        color: config.color,
        resolution: config.resolution || 50,
        visible: true,
        geometry: null,
        material: new THREE.MeshBasicMaterial({
          color: new THREE.Color(config.color),
          wireframe: false,
          transparent: true,
          opacity: 0.8,
          side: THREE.DoubleSide,
        }),
        compiledFn: null,
        dependencies: new Set(),
      })

      state.formulas.push(newFormula)
      this.updateFormula(newFormula)
    },

    updateFormula(formula) {
      try {
        // Parse and compile
        const expr = formula.expression.split('=')[1].trim()
        formula.compiledFn = compile(expr)

        // Analyze dependencies
        const deps = analyzeDependencies(expr)
        formula.dependencies = new Set(deps)

        // Update dependency graph
        this.updateDependencyGraph(formula.id, deps)

        // Regenerate geometry
        this.generateGeometry(formula)
      }
      catch (error) {
        console.error('Formula update failed:', error)
      }
    },

    updateDependencyGraph(formulaId, dependencies) {
      // Remove old dependencies
      state.dependencyGraph.forEach((deps, key) => {
        state.dependencyGraph.set(
          key,
          deps.filter((id) => id !== formulaId),
        )
      })

      // Add new dependencies
      dependencies.forEach((depId) => {
        if (!state.dependencyGraph.has(depId)) {
          state.dependencyGraph.set(depId, [])
        }
        state.dependencyGraph.get(depId).push(formulaId)
      })
    },

    generateGeometry(formula) {
      if (formula.geometry) {
        formula.geometry.dispose()
      }

      formula.geometry = createParametricGeometry(
        (x, y) => this.evaluateFormula(formula, x, y),
        formula.resolution,
      )
    },

    evaluateFormula(formula, x, y) {
      const scope = { x, y }
      formula.dependencies.forEach((depId) => {
        const depFormula = state.formulas.find((f) => f.id === depId)
        if (depFormula) {
          scope[depId] = (x, y) => this.evaluateFormula(depFormula, x, y)
        }
      })

      try {
        return formula.compiledFn.evaluate(scope)
      }
      catch (error) {
        console.error('Evaluation error:', error)
        return 0
      }
    },
  }

  return { ...state, ...getters, ...actions }
})
